package com.example.asm;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;


public class Main {

    public static void main(String[] args) {
        Main main = new Main();
        main.test();
        System.out.println("---------------------------------");

        String className = "com.example.asm.Main3";
        String methodName = "test";
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        //声明一个类
        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, className.replace(".","/"), null, "java/lang/Object", null);
        //Main main = new Main();
        MethodVisitor constructor = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        constructor.visitVarInsn(Opcodes.ALOAD, 0);
        //执行父类的init初始化
        constructor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
        //设置当前方法的返回值
        constructor.visitInsn(Opcodes.RETURN);
        // 设置栈大小和局部变量表大小
        constructor.visitMaxs(1, 1);
        constructor.visitEnd();
        //执行test方法
        MethodVisitor runMethod = cw.visitMethod(Opcodes.ACC_PUBLIC, methodName, "()V", null, null);
        //先获取一个java.io.PrintStream对象
        runMethod.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        //将int, float或String型常量值从常量池中推送至栈顶 (此处将message字符串从常量池中推送至栈顶[输出的内容])
        runMethod.visitLdcInsn("我是"+className+"的"+methodName);
        //执行println方法（执行的是参数为字符串，无返回值的println函数）
        runMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        runMethod.visitInsn(Opcodes.RETURN);
        runMethod.visitMaxs(2, 1);
        runMethod.visitEnd();
        cw.visitEnd();
        byte[] bytes = cw.toByteArray();
        MyClassLoader myClassLoader = new MyClassLoader();
        Class<?> aClass = myClassLoader.defineClass(className, bytes);
        try {
            Object o = aClass.newInstance();
            aClass.getMethod(methodName).invoke(o);
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    public void test(){
        System.out.println("Hello World!!");
    }

    static class MyClassLoader extends ClassLoader {
        public Class<?> defineClass(String name, byte[] b) {
        // ClassLoader是个抽象类，而ClassLoader.defineClass 方法是protected的 // 所以我们需要定义一个子类将这个方法暴露出来
            return super.defineClass(name, b, 0, b.length);
        }
    }

}
